package dyncallback

/*
#include "../c/dyncallback/dyncall_callback.h"
#include "bridge/_tool_.h"
*/
import "C"
import "unsafe"

const (
	Void int32 = iota
	Boolean
	Char
	SingedChar
	UnsignedChar
	Short
	UnsignedShort
	Int
	UnsignedInt
	Long
	UnsignedLong
	Float
	Double
	Pointer
	String
)

//export goCallback
func goCallback(pcb *C.DCCallback, args *C.DCArgs, result *C.DCValue, userData *C.void) C.char {
	info := functionInfos[C.GoString((*C.char)(unsafe.Pointer(userData)))]
	if info == nil {
		return C.char('s')
	}
	var goArgs []interface{}
	var value interface{}
	for i, length := 0, len(info.argsTypes); i < length; i++ {
		switch info.argsTypes[i] {
		case Void:
			value = nil
		case Boolean:
			if int(C.dcbArgBool(args)) == 0 {
				value = false
			} else {
				value = true
			}
		case Char:
			value = rune(C.dcbArgChar(args))
		case UnsignedChar:
			value = int8(C.dcbArgChar(args))
		case SingedChar:
			value = uint8(C.dcbArgUChar(args))
		case Short:
			value = int16(C.dcbArgShort(args))
		case UnsignedShort:
			value = uint16(C.dcbArgUShort(args))
		case Int:
			value = int32(C.dcbArgInt(args))
		case UnsignedInt:
			value = uint32(C.dcbArgUInt(args))
		case Long:
			value = uint64(C.dcbArgLong(args))
		case UnsignedLong:
			value = uint64(C.dcbArgULong(args))
		case Float:
			value = float32(C.dcbArgFloat(args))
		case Double:
			value = float64(C.dcbArgDouble(args))
		case Pointer:
			value = unsafe.Pointer(C.dcbArgPointer(args))
		case String:
			value = C.GoString((*C.char)(C.dcbArgPointer(args)))
		default:
			value = nil
		}
		if value == nil {
			continue
		}
		goArgs = append(goArgs, value)
	}
	value = info.function(info.userData, goArgs...)
	switch info.returnType {
	case Boolean:
		if value.(bool) {
			C.setBoolean(result, C.DCbool(1))
		} else {
			C.setBoolean(result, C.DCbool(0))
		}
	case Char:
		C.setChar(result, C.DCchar(value.(rune)))
	case UnsignedChar:
		C.setUnsignedChar(result, C.DCuchar(value.(uint8)))
	case SingedChar:
		C.setChar(result, C.DCchar(value.(int8)))
	case Short:
		C.setShort(result, C.DCshort(value.(int16)))
	case UnsignedShort:
		C.setUnsignedShort(result, C.DCushort(value.(uint16)))
	case Int:
		if currentValue, ok := value.(int); ok {
			C.setInt(result, C.DCint(currentValue))
		} else {
			C.setInt(result, C.DCint(value.(int32)))
		}
	case UnsignedInt:
		if currentValue, ok := value.(uint); ok {
			C.setUnsignedInt(result, C.DCuint(currentValue))
		} else {
			C.setUnsignedInt(result, C.DCuint(value.(uint32)))
		}
	case Long:
		C.setLong(result, C.DClong(value.(int64)))
	case UnsignedLong:
		C.setUnsignedLong(result, C.DCulong(value.(uint64)))
	case Float:
		C.setFloat(result, C.DCfloat(value.(float32)))
	case Double:
		C.setDouble(result, C.DCdouble(value.(float64)))
	case Pointer:
		C.setPointer(result, C.DCpointer(value.(unsafe.Pointer)))
	case String:
		C.setString(result, C.DCstring(C.CString(value.(string))))
	}
	return C.char('s')
}
